//
// C++ Implementation: gpl
//
// Description: Simple dumping monitor.
//
//
// Author: Andrey Sharoyko <vanyamboe@gmail.com>, (C) 2011
//
// Copyright: See COPYING file that comes with this distribution
//
//
#include "dumpdebug.h"
//#include <HardwareSerial.h>
#include "printpgm.h"
#include "registers.h"
#include "dumpmon.h"
#include "memory.h"
#include "timers.h"
#include "interrupts.h"

prog_char usage[] PROGMEM = {
    "\r\nSimple Dumping Monitor. Version " DUMPMON_VERSION "\r\nCommands:\r\n"
    "?\tdisplay this help\r\n"
    "R\tdump registers\r\n"
    "I\tdump interrupts map\r\n"
    "W\tdump debug watches\r\n"
    "V\tshow available RAM\r\n"
    "M<addr>\tdump RAM (ie. M000"
#if (DUMP_RAM_ADDR_LEN == 4)
    "0"
#endif
    ")\r\n"
    "F<addr>\tdump Flash (ie. F0000"
#if (DUMP_FLASH_ADDR_LEN == 5)
    "0"
#endif
    ")\r\n"
    "O<addr>\tdump EEPROM (ie. O0000"
#if (DUMP_EEPROM_ADDR_LEN == 5)
    "0"
#endif
    ")\r\n"
    "T<num>\tdump timer state (ie. T1)\r\n"
    "S\tstep-execution\r\n"
    "G\tgo-execution\r\n"
    "N\tstep up to next breakpoint (step-mode)\r\n"
    "A\trepeat last command (again)\r\n"
    "C\tcontinue last dump"
};
prog_char nUnknownCommand[] PROGMEM = {
     "\r\nUnknown command: "
};
prog_char nIllegalCharacter[] PROGMEM = {
      "\r\nIllegal character in address: "
};
prog_char unknownState[] PROGMEM = {
      "Unknown state."
};

#define DUMP_PART_LEN 0x100

state_t monState = IDLE;
state_t lastFunc = IDLE;
dump_addr_t monAddr;
unsigned char nAddr;
unsigned char addrLen;

void dumpmonSetup(unsigned int serialSpd, DebugState state)
{
  debugState = state;
  Serial.begin(serialSpd);
}

void dispatchStateIdle(char cmd)
{
  if (cmd < ' ')
    return;
  
  if (cmd >= 'A' && cmd <= 'Z')
    cmd += 'a' - 'A';
  
  if (cmd == 'c') {
    switch (lastFunc) {
      case REGS:
      case IRQS:
      case WATCHES:
      case RAMAVAIL:
        cmd = 'a';
        break;
      case RAM:
      case FLASH:
      case ROM:
        monAddr += (dump_addr_t) DUMP_PART_LEN;
        cmd = 'a';
        break;
      case TIMER:
        if(++monAddr == DUMP_TIMERS_COUNT)
          monAddr = 0;
        cmd = 'a';
        break;
    }
  }
  
  switch(cmd) {
    case '?':
      displayUsage();
      break;
     
     case 'r':
       lastFunc = REGS;
       dumpRegisters();
       break;
      
     case 'i':
       lastFunc = IRQS;
       dumpInterrupts();
       break;
      
     case 'w':
       lastFunc = WATCHES;
       dumpWatches();
       break;
      
     case 'v':
       lastFunc = RAMAVAIL;
       availableMemory();
       break;
      
      case 'm':
        monState = RAM;
        prepareAddress(DUMP_RAM_ADDR_LEN);
        break;
      
      case 'f':
        monState = FLASH;
        prepareAddress(DUMP_FLASH_ADDR_LEN);
        break;
      
      case 'o':
        monState = ROM;
        prepareAddress(DUMP_EEPROM_ADDR_LEN);
        break;
      
      case 't':
        monState = TIMER;
        prepareAddress(1);
        break;
        
      case 'a':
        switch(lastFunc) {
          case REGS:
            dumpRegisters();
            break;
          case IRQS:
            dumpInterrupts();
            break;
          case WATCHES:
            dumpWatches();
            break;
          case RAMAVAIL:
            availableMemory();
            break;
          case RAM:
          case FLASH:
          case ROM:
            dumpMemory(monAddr, lastFunc);
            break;
          case TIMER:
            dumpTimer(monAddr);
            break;
        }
        break;
        
     case 'g':
       debugState = GO_MODE;
       nextStep = false;
       Serial.println();
       break;
      
     case 's':
       debugState = STEP_MODE;
       nextStep = true;
       Serial.println();
       break;
      
     case 'n':
       debugState = STEP_MODE;
       nextStep = true;
       Serial.println();
       break;
      
      default:
	printPgm(nUnknownCommand);
        Serial.println(cmd);
  }
}

void displayUsage()
{
  printlnPgm(usage);
}

void prepareAddress(unsigned char len)
{
  monAddr = 0;
  nAddr = 0;
  addrLen = len;
}

bool enterAddr(char cb)
{
  if( ! ((cb >= '0' && cb <= '9') || (cb >= 'a' && cb <='f') || (cb >= 'A' && cb <='F'))) {
    printPgm(nIllegalCharacter);
    Serial.print(cb);
    monState = ERROR;
    return false;
  }
  monAddr <<= 4;
  if(cb >= '0' && cb <= '9')
    monAddr += cb - '0';
  else if(cb >= 'a' && cb <='f')
    monAddr += cb - 'a' + 10;
  else if(cb >= 'A' && cb <='F')
    monAddr += cb - 'A' + 10;
  ++nAddr;
  return (nAddr == addrLen);
}

void dumpmonLoop()
{
    char cmd;
    uint8_t nChars = Serial.available();
    for ( ; nChars != 0; --nChars) {
        cmd = Serial.read();
        Serial.print(cmd);
        switch(monState) {
            case IDLE:
                dispatchStateIdle(cmd);
                break;

            case RAM:
            case FLASH:
            case ROM:
                if(enterAddr(cmd)) {
                    dumpMemory(monAddr, monState);
                    lastFunc = monState;
                    monState = IDLE;
                }
                break;

            case TIMER:
                if(enterAddr(cmd)) {
                    dumpTimer(monAddr);
                    lastFunc = monState;
                    monState = IDLE;
                }
                break;

            default:
                printlnPgm(unknownState);
                monState = IDLE;
        }
        if (monState == ERROR)
            monState = IDLE;
        if (monState == IDLE)
            Serial.print('>');
    }
}  
